# Setup Windows Service

## Run a Console Application as a Windows Service

To run a Console Application as a Windows Service, you need to:

- Install the `Topshelf` NuGet package.
- Setup [Topshelf](http://topshelf-project.com/) ([documentation](https://topshelf.readthedocs.io/en/latest/)).

```csharp
using Topshelf;

public class Program
{
    public static void Main()
    {
        HostFactory.Run(x =>                                 //1
        {
            x.Service<TownCrier>(s =>                        //2
            {
               s.ConstructUsing(name=> new TownCrier());     //3
               s.WhenStarted(tc => tc.Start());              //4
               s.WhenStopped(tc => tc.Stop());               //5
            });

            x.RunAsLocalSystem();                            //6

            x.SetDescription("Sample Topshelf Host");        //7
            x.SetDisplayName("Stuff");                       //8
            x.SetServiceName("Stuff");                       //9
        });                                                  //10
    }
}

public class TownCrier
{
    readonly Timer _timer;
    public TownCrier()
    {
        _timer = new Timer(1000) {AutoReset = true};
        _timer.Elapsed += (sender, eventArgs) => Console.WriteLine("It is {0} and all is well", DateTime.Now);
    }
    public void Start() { _timer.Start(); }
    public void Stop() { _timer.Stop(); }
}
```

Now for the play by play:

1. Here we are setting up the host using the HostFactory.Run the runner. We open up a new lambda where the 'x' in this case exposes all of the host level configuration. Using this approach the command arguments are extracted from environment variables.
2. Here we are telling Topshelf that there is a service of type 'TownCrier'. The lambda that gets opened here is exposing the service configuration options through the 's' parameter.
3. This tells Topshelf how to build an instance of the service. Currently we are just going to 'new it up' but we could just as easily pull it from an IoC container with some code that would look something like 'container.GetInstance<TownCrier>()'
4. How does Topshelf start the service
5. How does Topshelf stop the service
6. Here we are setting up the 'run as' and have selected the 'local system'. We can also set up from the command line Interactively with a win from type prompt and we can also just pass in some username/password as string arguments
7. Here we are setting up the description for the winservice to be use in the windows service monitor
8. Here we are setting up the display name for the winservice to be use in the windows service monitor
9. Here we are setting up the service name for the winservice to be use in the windows service monitor
10. Now that the lambda has closed, the configuration will be executed and the host will start running.

## Owin self hosted web app

To setup a Owin self hosted web app, you need to:

- Create `Startup.cs` class:

```csharp
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        using (var config = new HttpConfiguration())
        {
            /// other configurations go here

            app.UseWebApi(config);
        }
    }
}
```

- Install `Sustainalytics.Owin.Extensions` NuGet package.
- Setup `Topshelf` to use the `WebAppService` & `Startup` classes: 

```csharp
using Sustainalytics.Owin.Extensions;
using Topshelf;

public static class Program
{
    private const string ServiceName = "Sustainalytics.DSO.Api.Service";

    public static void Main()
    {
        HostFactory.Run(x =>
        {
            x.Service<WebAppService>(sc =>
            {
                sc.ConstructUsing(() => new WebAppService());
                sc.WhenStarted(s => s.Start<Startup>());
                sc.WhenStopped(s => s.Stop());
            });

            x.RunAsLocalSystem();

            x.SetServiceName(ServiceName);
            x.SetDisplayName(ServiceName);
            x.SetDescription("Hosts the " + ServiceName.Replace(".", " "));
        });
    }
}
```

## App.config settings & transformations

- Install `SlowCheetah` NuGet package in order to enable `App.config` transformations.

- In `App.config` set the `HostUrl` & `HttpsPort` appsettings.

- In `App.Debug.config` configure the web app to use `http` with the following lines:

```xml
  <appSettings>
    <add key ="AllowInsecureHttp" value="true" xdt:Transform="InsertIfMissing" xdt:Locator="Match(key)" />
  </appSettings>
```

- In `App.Test.config` & `App.Stage.config` setup config transformations with the following lines:

```xml
  <appSettings>
    <add key="HostUrl" value="https://newUrl" xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
  </appSettings>
```

- Set `Copy to Output Directory` for all `App.config` files (Debug, Release, Test, Stage) to `Copy if newer` or `Copy always`

##  OAuth2 authentication

To use OAuth2 authentication in the app, you need to:
- Install the `Sustainalytics.Auth.Common` NuGet package.
- Add `using Sustainalytics.Auth.Common;` in the Startup class.
- Add `app.UseAuthenticationServer();` in the Startup class.

## Claims

To use claims in the app, you need to:
- Install the `Sustainalytics.Auth.Claims` NuGet package.
- Use claims extension methods (`using Sustainalytics.Auth.Claims;`).

Example:

```csharp
claims.GetUniverses(); // gets all universes from claims
claims.GetGuidValue(CustomClaimTypes.AccountId); // gets AccountId from claims
claims.GetStringValue(CustomClaimTypes.(AccountName); // gets AccountName from claims
``` 

# Build & deploy automation

To enable build & deploy automation, you need to:

- Install `OctoPack` NuGet package.
- Add [Cake](http://cakebuild.net/) build scripts ([documentation](http://cakebuild.net/docs)):
  - `build.ps1` script (this file should not be changed);
  - `build.cake` script (this is where you can define the build steps);
  - `Service.nuspec` (manifest that uses XML to describe a package; [documentation](https://docs.nuget.org/create/nuspec-reference)); 
  
To add `build.ps1` run the following command in a PowerShell window:

```
Invoke-WebRequest http://cakebuild.net/download/bootstrapper/windows -OutFile build.ps1
```

Example `Service.nuspec`:

```xml
<?xml version="1.0"?>
<package>
  <metadata>
    <id>Sustainalytics.DSO.Api.Service</id>
    <version>$version$</version>
    <authors>Sustainalytics</authors>
    <description>Sustainalytics.DSO.Api.Service</description>
  </metadata>
</package>
```

Example `build.cake` script:

```csharp
#tool "nuget:?package=xunit.runner.console"
#tool "nuget:?package=OctopusTools"

var target = Argument("target", "Default");
var configuration = Argument("configuration", "Release");
var releaseNumber = Argument("releaseNumber", "");

var solution = "DSO.sln";
var octopusDeployProjectName = "DSO";
var octopusDeployServer = "http://10.120.0.53:8082";
var octopusDeployApiKey = "API-MBFQMNHSNQE2OQHPSBIQH3MKEY";

var buildDir = "./**/bin/" + configuration;

Task("Clean")
  .Does(() => CleanDirectories(buildDir));

Task("NuGetRestore")
  .Does(() => NuGetRestore(solution));

Task("Build")
  .Does(() => MSBuild(solution, new MSBuildSettings {
    Configuration = configuration,
    ArgumentCustomization = args => args
      .Append("/p:RunOctoPack=true")
      .Append("/p:OctoPackPackageVersion=" + releaseNumber)
  }));

var xunit2Settings = new XUnit2Settings {
    Parallelism = ParallelismOption.All,
    HtmlReport = false,
    NoAppDomain = true,
};

xunit2Settings.ExcludeTrait("Category", new[] { "Ignore" });

Task("RunUnitTests")
  .Does(() => XUnit2(buildDir + "/*.Tests.dll", xunit2Settings));

Task("NuGetPush")
  .WithCriteria(HasArgument("releaseNumber"))
  .Does(() => {
    var packages = GetFiles("./**/octopacked/*.nupkg").ToList();
    packages.ForEach(package => NuGetPush(package, new NuGetPushSettings {
      Source = octopusDeployServer + "/nuget/packages",
      ApiKey = octopusDeployApiKey,
    }));
  });

Task("OctoCreateRelease")
  .WithCriteria(HasArgument("releaseNumber"))
  .Does(() => OctoCreateRelease(octopusDeployProjectName, new CreateReleaseSettings {
    Server = octopusDeployServer,
    ApiKey = octopusDeployApiKey,
    ReleaseNumber = releaseNumber,
  }));

Task("Default")
  .IsDependentOn("Clean")
  .IsDependentOn("NuGetRestore")
  .IsDependentOn("Build")
  .IsDependentOn("RunUnitTests");

Task("PrepareDeploy")
  .IsDependentOn("Default")
  .IsDependentOn("NuGetPush")
  .IsDependentOn("OctoCreateRelease");

RunTarget(target);
```

To run the build scrips locally from the command line use the following commands:
- use default arguments: `powershell ./build.ps1`
- run the PrepareDeploy with a releaseNumber: `powershell ./build.ps1 -target PrepareDeploy -releaseNumber='\"1.2.3\"'` 
